// mainframe.cpp

#include "stdafx.h"

#include <shlobj.h>
#include <shlguid.h>
#include <ntquery.h>

#include "mainfrm.h"


BOOL CMainFrame::FillListView(LPTVITEMDATA lptvid, LPSHELLFOLDER pShellFolder)
{
    ATLASSERT(pShellFolder != NULL);
    CComPtr<IEnumIDList> spEnumIDList;
    HRESULT hr = pShellFolder->EnumObjects(m_wndListView.GetParent(), SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spEnumIDList);

    if (FAILED(hr))
        return FALSE;

    CShellItemIDList lpifqThisItem;
    CShellItemIDList lpi;
    ULONG ulFetched = 0;
    UINT uFlags = 0;
    LVITEM lvi = { 0 };
    lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
    int iCtr = 0;

    while (spEnumIDList->Next(1, &lpi, &ulFetched) == S_OK) {
        // Get some memory for the ITEMDATA structure.
        LPLVITEMDATA lplvid = new LVITEMDATA;

        if (lplvid == NULL)
            return FALSE;

        // Since you are interested in the display attributes as well as other attributes,
        // you need to set ulAttrs to SFGAO_DISPLAYATTRMASK before calling GetAttributesOf()
        ULONG ulAttrs = SFGAO_DISPLAYATTRMASK;
        hr = pShellFolder->GetAttributesOf(1, (const struct _ITEMIDLIST **)&lpi, &ulAttrs);

        if (FAILED(hr))
            return FALSE;

        lplvid->ulAttribs = ulAttrs;
        lpifqThisItem = m_ShellMgr.ConcatPidls(lptvid->lpifq, lpi);
        lvi.iItem = iCtr;
        lvi.iSubItem = 0;
        lvi.pszText = LPSTR_TEXTCALLBACK;
        lvi.cchTextMax = MAX_PATH;
        uFlags = SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON;
        lvi.iImage = I_IMAGECALLBACK;
        lplvid->spParentFolder.p = pShellFolder;
        pShellFolder->AddRef();
        // Now make a copy of the ITEMIDLIST
        lplvid->lpi = m_ShellMgr.CopyITEMID(lpi);
        lvi.lParam = (LPARAM)lplvid;
        // Add the item to the list view control
        int n = m_wndListView.InsertItem(&lvi);
        m_wndListView.AddItem(n, 1, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK);
        m_wndListView.AddItem(n, 2, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK);
        m_wndListView.AddItem(n, 3, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK);
        m_wndListView.AddItem(n, 4, LPSTR_TEXTCALLBACK, I_IMAGECALLBACK);
        iCtr++;
        lpifqThisItem = NULL;
        lpi = NULL;   // free PIDL the shell gave you
    }

    SortData sd(m_nSort, m_bReverseSort);
    m_wndListView.SortItems(CMainFrame::ListViewCompareProc, (LPARAM)&sd);
    return TRUE;
}

int CALLBACK CMainFrame::ListViewCompareProc(LPARAM lparam1, LPARAM lparam2, LPARAM lParamSort)
{
    ATLASSERT(lParamSort != NULL);
    LPLVITEMDATA lplvid1 = (LPLVITEMDATA)lparam1;
    LPLVITEMDATA lplvid2 = (LPLVITEMDATA)lparam2;
    SortData* pSD = (SortData*)lParamSort;
    HRESULT hr = 0;

    if (pSD->bReverseSort)
        hr = lplvid1->spParentFolder->CompareIDs(0, lplvid2->lpi, lplvid1->lpi);
    else
        hr = lplvid1->spParentFolder->CompareIDs(0, lplvid1->lpi, lplvid2->lpi);

    return (int)(short)HRESULT_CODE(hr);
}

HRESULT CMainFrame::FillTreeView(IShellFolder* pShellFolder, LPITEMIDLIST lpifq, HTREEITEM hParent)
{
    if (pShellFolder == NULL)
        return E_POINTER;

    CComPtr<IEnumIDList> spIDList;
    HRESULT hr = pShellFolder->EnumObjects(m_hWnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &spIDList);

    if (FAILED(hr))
        return hr;

    CShellItemIDList lpi;
    CShellItemIDList lpifqThisItem;
    LPTVITEMDATA lptvid = NULL;
    ULONG ulFetched = 0;
    TCHAR szBuff[256] = { 0 };
    TVITEM tvi = { 0 };             // TreeView Item
    TVINSERTSTRUCT tvins = { 0 };   // TreeView Insert Struct
    HTREEITEM hPrev = NULL;         // Previous Item Added
    COMBOBOXEXITEM cbei = { 0 };
    // Hourglass on
    CWaitCursor wait;
    int iCnt = 0;

    while (spIDList->Next(1, &lpi, &ulFetched) == S_OK) {
        // Create a fully qualified path to the current item
        // The SH* shell api's take a fully qualified path pidl,
        // (see GetIcon above where I call SHGetFileInfo) whereas the
        // interface methods take a relative path pidl.
        ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER;
        pShellFolder->GetAttributesOf(1, (LPCITEMIDLIST*)&lpi, &ulAttrs);

        if ((ulAttrs & (SFGAO_HASSUBFOLDER | SFGAO_FOLDER)) != 0) {
            // We need this next if statement so that we don't add things like
            // the MSN to our tree. MSN is not a folder, but according to the
            // shell is has subfolders....
            if (ulAttrs & SFGAO_FOLDER) {
                tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
                cbei.mask = CBEIF_TEXT | CBEIF_INDENT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;

                if (ulAttrs & SFGAO_HASSUBFOLDER) {
                    // This item has sub-folders, so let's put the + in the TreeView.
                    // The first time the user clicks on the item, we'll populate the
                    // sub-folders then.
                    tvi.cChildren = 1;
                    tvi.mask |= TVIF_CHILDREN;
                }

                // OK, let's get some memory for our ITEMDATA struct
                lptvid = new TVITEMDATA;

                if (lptvid == NULL)
                    return E_FAIL;

                // Now get the friendly name that we'll put in the treeview...
                if (!m_ShellMgr.GetName(pShellFolder, lpi, SHGDN_NORMAL, szBuff))
                    return E_FAIL;

                tvi.pszText = szBuff;
                tvi.cchTextMax = MAX_PATH;
                cbei.pszText = szBuff;
                cbei.cchTextMax = MAX_PATH;
                lpifqThisItem = m_ShellMgr.ConcatPidls(lpifq, lpi);
                // Now, make a copy of the ITEMIDLIST
                lptvid->lpi = m_ShellMgr.CopyITEMID(lpi);
                m_ShellMgr.GetNormalAndSelectedIcons(lpifqThisItem, &tvi);
                lptvid->spParentFolder.p = pShellFolder;    // Store the parent folders SF
                pShellFolder->AddRef();
                lptvid->lpifq = m_ShellMgr.ConcatPidls(lpifq, lpi);
                tvi.lParam = (LPARAM)lptvid;
                tvins.item = tvi;
                tvins.hInsertAfter = hPrev;
                tvins.hParent = hParent;
                // Add the item to the tree and combo
                hPrev = TreeView_InsertItem(m_wndTreeView.m_hWnd, &tvins);
                cbei.iItem = iCnt++;
                cbei.iImage = tvi.iImage;
                cbei.iSelectedImage = tvi.iSelectedImage;
                int nIndent = 0;

                while (NULL != (hPrev = (HTREEITEM)m_wndTreeView.SendMessage(TVM_GETNEXTITEM, TVGN_PARENT, (LPARAM)hPrev))) {
                    nIndent++;
                }

                cbei.iIndent = nIndent;
                m_wndCombo.SendMessage(CBEM_INSERTITEM, 0, (LPARAM)&cbei);
            }

            lpifqThisItem = NULL;
        }

        lpi = NULL;   // Finally, free the pidl that the shell gave us...
    }

    return hr;
}

int CALLBACK CMainFrame::TreeViewCompareProc(LPARAM lparam1, LPARAM lparam2, LPARAM /*lparamSort*/)
{
    LPTVITEMDATA lptvid1 = (LPTVITEMDATA)lparam1;
    LPTVITEMDATA lptvid2 = (LPTVITEMDATA)lparam2;
    HRESULT hr = lptvid1->spParentFolder->CompareIDs(0, lptvid1->lpi, lptvid2->lpi);
    return (int)(short)HRESULT_CODE(hr);
}

HWND CMainFrame::CreateAddressBarCtrl(HWND hWndParent)
{
    RECT rc = { 50, 0, 300, 100 };
    m_wndCombo.Create(hWndParent, rc, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBS_DROPDOWN | CBS_AUTOHSCROLL);
    m_wndCombo.SetFont(AtlGetDefaultGuiFont());
    return m_wndCombo;
}

LRESULT CMainFrame::OnCreate(UINT, WPARAM, LPARAM, BOOL&)
{
    // create command bar window
    RECT rcCmdBar = { 0, 0, 100, 100 };
    HWND hWndCmdBar = m_wndCmdBar.Create(m_hWnd, rcCmdBar, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
    // atach menu
    m_wndCmdBar.AttachMenu(GetMenu());
    // load command bar images
    m_wndCmdBar.LoadImages(IDR_MAINFRAME);
    // remove old menu
    SetMenu(NULL);
    HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);
    HWND hWndAddressBar = CreateAddressBarCtrl(m_hWnd);
    CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
    AddSimpleReBarBand(hWndCmdBar);
    AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
    AddSimpleReBarBand(hWndAddressBar, _T("Address"), TRUE);
    CreateSimpleStatusBar();
    m_hWndClient = m_wndSplitter.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
    m_wndFolderTree.Create(m_wndSplitter, _T("Folders"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
    m_wndTreeView.Create(m_wndFolderTree, rcDefault, NULL,
                         WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
                         TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS | TVS_SHOWSELALWAYS,
                         WS_EX_CLIENTEDGE);
    m_wndFolderTree.SetClient(m_wndTreeView);
    m_wndListView.Create(m_wndSplitter, rcDefault, NULL,
                         WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
                         LVS_REPORT | LVS_AUTOARRANGE | LVS_SHOWSELALWAYS | LVS_SHAREIMAGELISTS,
                         WS_EX_CLIENTEDGE);
    m_wndListView.SetExtendedListViewStyle(LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE | LVS_EX_UNDERLINEHOT);
    InitViews();
    UpdateLayout();
    m_wndSplitter.SetSplitterPanes(m_wndFolderTree, m_wndListView);
    RECT rect;
    GetClientRect(&rect);
    m_wndSplitter.SetSplitterPos((rect.right - rect.left) / 4);
    UIAddToolBar(hWndToolBar);
    UISetCheck(ID_VIEW_TOOLBAR, 1);
    UISetCheck(ID_VIEW_ADDRESS_BAR, 1);
    UISetCheck(ID_VIEW_STATUS_BAR, 1);
    UISetCheck(ID_VIEW_DETAILS, 1);
    UISetCheck(ID_VIEW_SORT_NAME, 1);
    CMessageLoop* pLoop = _Module.GetMessageLoop();
    pLoop->AddMessageFilter(this);
    pLoop->AddIdleHandler(this);
    return 0;
}

LRESULT CMainFrame::OnViewChange(WORD, WORD wID, HWND, BOOL&)
{
    UISetCheck(ID_VIEW_ICONS, FALSE);
    UISetCheck(ID_VIEW_SMALL_ICONS, FALSE);
    UISetCheck(ID_VIEW_LIST, FALSE);
    UISetCheck(ID_VIEW_DETAILS, FALSE);
    UISetCheck(wID, TRUE);
    DWORD dwNewStyle = (int)wID - ID_VIEW_ICONS;
    m_wndListView.SetViewType(dwNewStyle);
    return 0;
}

LRESULT CMainFrame::OnComboGo(WORD, WORD, HWND, BOOL&)
{
//  TCHAR szBuff[MAX_PATH] = { 0 };
//  m_wndCombo.GetWindowText(szBuff, MAX_PATH);
//
//  m_wndTreeView.SelectItem(NULL);
    //m_wndCombo.SetEditSel(0, -1);
    //FillListView(m_wndListView, szBuff);
    MessageBox(_T("Not yet implemented!"), _T("WTL Explorer"), MB_OK | MB_ICONINFORMATION);
    return 0;
}

LRESULT CMainFrame::OnViewRefresh(WORD, WORD, HWND, BOOL&)
{
    MessageBox(_T("Not yet implemented!"), _T("WTL Explorer"), MB_OK | MB_ICONINFORMATION);
    return 0;
}

LRESULT CMainFrame::OnViewSort(WORD, WORD wID, HWND, BOOL&)
{
    UISetCheck(ID_VIEW_SORT_NAME, FALSE);
    UISetCheck(ID_VIEW_SORT_SIZE, FALSE);
    UISetCheck(ID_VIEW_SORT_TYPE, FALSE);
    UISetCheck(ID_VIEW_SORT_TIME, FALSE);
    UISetCheck(ID_VIEW_SORT_ATTR, FALSE);
    UISetCheck(wID, TRUE);
    m_bReverseSort = false;
    m_nSort = (int)wID - ID_VIEW_SORT_NAME;
    ATLASSERT(m_nSort >= 0 && m_nSort <= 4);
    SortData sd(m_nSort, m_bReverseSort);
    m_wndListView.SortItems(CMainFrame::ListViewCompareProc, (LPARAM)&sd);
    return 0;
}

LRESULT CMainFrame::OnTVSelChanged(int, LPNMHDR pnmh, BOOL&)
{
    LPNMTREEVIEW lpTV = (LPNMTREEVIEW)pnmh;
    LPTVITEMDATA lptvid = (LPTVITEMDATA)lpTV->itemNew.lParam;

    if (lptvid != NULL) {
        CComPtr<IShellFolder> spFolder;
        HRESULT hr = lptvid->spParentFolder->BindToObject(lptvid->lpi, 0, IID_IShellFolder, (LPVOID *)&spFolder);

        if (FAILED(hr))
            return hr;

        if (m_wndListView.GetItemCount() > 0)
            m_wndListView.DeleteAllItems();

        FillListView(lptvid, spFolder);
        TCHAR psz[MAX_PATH] = { 0 };
        m_ShellMgr.GetName(lptvid->spParentFolder, lptvid->lpi, SHGDN_FORPARSING, psz);

        if (StrChr(psz, _T('{')))  //special case
            m_ShellMgr.GetName(lptvid->spParentFolder, lptvid->lpi, SHGDN_NORMAL, psz);

        int nImage = 0;
        int nSelectedImage = 0;
        m_wndTreeView.GetItemImage(lpTV->itemNew.hItem, nImage, nSelectedImage);
        COMBOBOXEXITEM cbei;
        cbei.mask = CBEIF_TEXT | CBEIF_INDENT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
        cbei.iItem = -1; //Editbox of the comboboxex
        cbei.pszText = psz;
        cbei.cchTextMax = MAX_PATH;

        if (m_wndTreeView.ItemHasChildren(lpTV->itemNew.hItem)) {
            cbei.iImage = nImage;
            cbei.iSelectedImage = nImage;
        } else {
            cbei.iImage = nSelectedImage;
            cbei.iSelectedImage = nSelectedImage;
        }

        cbei.iIndent = 1;
        m_wndCombo.SetItem(&cbei);
    }

    return 0L;
}

LRESULT CMainFrame::OnTVItemExpanding(int, LPNMHDR pnmh, BOOL&)
{
    CWaitCursor wait;
    LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)pnmh;

    if ((pnmtv->itemNew.state & TVIS_EXPANDEDONCE))
        return 0L;

    LPTVITEMDATA lptvid = (LPTVITEMDATA)pnmtv->itemNew.lParam;
    CComPtr<IShellFolder> spFolder;
    HRESULT hr = lptvid->spParentFolder->BindToObject(lptvid->lpi, 0, IID_IShellFolder, (LPVOID *)&spFolder);

    if (FAILED(hr))
        return hr;

    FillTreeView(spFolder, lptvid->lpifq, pnmtv->itemNew.hItem);
    TVSORTCB tvscb;
    tvscb.hParent = pnmtv->itemNew.hItem;
    tvscb.lpfnCompare = CMainFrame::TreeViewCompareProc;
    tvscb.lParam = 0;
    TreeView_SortChildrenCB(m_wndTreeView.m_hWnd, &tvscb, 0);
    return 0L;
}

LRESULT CMainFrame::OnTVDeleteItem(int, LPNMHDR pnmh, BOOL&)
{
    LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)pnmh;
    LPTVITEMDATA lptvid = (LPTVITEMDATA)pnmtv->itemOld.lParam;
    delete lptvid;
    return 0;
}

LRESULT CMainFrame::OnLVGetDispInfo(int, LPNMHDR pnmh, BOOL&)
{
    NMLVDISPINFO* plvdi = (NMLVDISPINFO*)pnmh;

    if (plvdi == NULL)
        return 0L;

    LPLVITEMDATA lplvid = (LPLVITEMDATA)plvdi->item.lParam;
    HTREEITEM hti = m_wndTreeView.GetSelectedItem();
    TVITEM tvi = { 0 };
    tvi.mask = TVIF_PARAM;
    tvi.hItem = hti;
    m_wndTreeView.GetItem(&tvi);

    if (tvi.lParam <= 0)
        return 0L;

    LPTVITEMDATA lptvid = (LPTVITEMDATA)tvi.lParam;

    if (lptvid == NULL)
        return 0L;

    CShellItemIDList pidlTemp = m_ShellMgr.ConcatPidls(lptvid->lpifq, lplvid->lpi);
    plvdi->item.iImage = m_ShellMgr.GetIconIndex(pidlTemp, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);

    if (plvdi->item.iSubItem == 0 && (plvdi->item.mask & LVIF_TEXT)) {  // File Name
        m_ShellMgr.GetName(lplvid->spParentFolder, lplvid->lpi, SHGDN_NORMAL, plvdi->item.pszText);
    } else {
        CComPtr<IShellFolder2> spFolder2;
        HRESULT hr = lptvid->spParentFolder->QueryInterface(IID_IShellFolder2, (void**)&spFolder2);

        if (FAILED(hr))
            return hr;

        SHELLDETAILS sd = { 0 };
        sd.fmt = LVCFMT_CENTER;
        sd.cxChar = 15;
        hr = spFolder2->GetDetailsOf(lplvid->lpi, plvdi->item.iSubItem, &sd);

        if (FAILED(hr))
            return hr;

        if (sd.str.uType == STRRET_WSTR) {
            StrRetToBuf(&sd.str, lplvid->lpi.m_pidl, m_szListViewBuffer, MAX_PATH);
            plvdi->item.pszText = m_szListViewBuffer;
        } else if (sd.str.uType == STRRET_OFFSET) {
            plvdi->item.pszText = (LPTSTR)lptvid->lpi + sd.str.uOffset;
        } else if (sd.str.uType == STRRET_CSTR) {
            USES_CONVERSION;
            plvdi->item.pszText = A2T(sd.str.cStr);
        }
    }

    plvdi->item.mask |= LVIF_DI_SETITEM;
    return 0L;
}

LRESULT CMainFrame::OnLVColumnClick(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
    LPNMLISTVIEW lpLV = (LPNMLISTVIEW)pnmh;

    if (m_nSort == lpLV->iSubItem)
        m_bReverseSort = !m_bReverseSort;
    else
        m_nSort = lpLV->iSubItem;

    ATLASSERT(m_nSort >= 0 && m_nSort <= 4);
    SortData sd(m_nSort, m_bReverseSort);
    m_wndListView.SortItems(CMainFrame::ListViewCompareProc, (LPARAM)&sd);
    return 0;
}

LRESULT CMainFrame::OnNMRClick(int, LPNMHDR pnmh, BOOL&)
{
    POINT pt = { 0, 0 };
    ::GetCursorPos(&pt);
    POINT ptClient = pt;

    if (pnmh->hwndFrom != NULL)
        ::ScreenToClient(pnmh->hwndFrom, &ptClient);

    if (pnmh->hwndFrom == m_wndTreeView.m_hWnd) {
        TVHITTESTINFO tvhti = { 0 };
        tvhti.pt = ptClient;
        m_wndTreeView.HitTest(&tvhti);

        if ((tvhti.flags & TVHT_ONITEMLABEL) != 0) {
            TVITEM tvi = { 0 };
            tvi.mask = TVIF_PARAM;
            tvi.hItem = tvhti.hItem;

            if (m_wndTreeView.GetItem(&tvi) != FALSE) {
                LPTVITEMDATA lptvid = (LPTVITEMDATA)tvi.lParam;

                if (lptvid != NULL)
                    m_ShellMgr.DoContextMenu(::GetParent(m_wndTreeView.m_hWnd), lptvid->spParentFolder, lptvid->lpi, pt);
            }
        }
    } else if (pnmh->hwndFrom == m_wndListView.m_hWnd) {
        LVHITTESTINFO lvhti = { 0 };
        lvhti.pt = ptClient;
        m_wndListView.HitTest(&lvhti);

        if ((lvhti.flags & LVHT_ONITEMLABEL) != 0) {
            LVITEM lvi = { 0 };
            lvi.mask = LVIF_PARAM;
            lvi.iItem = lvhti.iItem;

            if (m_wndListView.GetItem(&lvi) != FALSE) {
                LPLVITEMDATA lptvid = (LPLVITEMDATA)lvi.lParam;

                if (lptvid != NULL)
                    m_ShellMgr.DoContextMenu(::GetParent(m_wndListView.m_hWnd), lptvid->spParentFolder, lptvid->lpi, pt);
            }
        }
    }

    return 0L;
}

LRESULT CMainFrame::OnLVItemClick(int, LPNMHDR pnmh, BOOL&)
{
    if (pnmh->hwndFrom != m_wndListView.m_hWnd)
        return 0L;

    POINT pt;
    ::GetCursorPos((LPPOINT)&pt);
    m_wndListView.ScreenToClient(&pt);
    LVHITTESTINFO lvhti;
    lvhti.pt = pt;
    m_wndListView.HitTest(&lvhti);
    LVITEM lvi;

    if (lvhti.flags & LVHT_ONITEM) {
        m_wndListView.ClientToScreen(&pt);
        lvi.mask = LVIF_PARAM;
        lvi.iItem = lvhti.iItem;
        lvi.iSubItem = 0;

        if (!m_wndListView.GetItem(&lvi))
            return 0;

        LPLVITEMDATA lplvid = (LPLVITEMDATA)lvi.lParam;

        if (lplvid == NULL) {
            return 0L;
        } else if ((lplvid->ulAttribs & SFGAO_FOLDER) == 0) {
            SHELLEXECUTEINFO sei = {
                sizeof(SHELLEXECUTEINFO),
                SEE_MASK_INVOKEIDLIST,               // fMask
                ::GetParent(m_wndListView.m_hWnd),   // hwnd of parent
                _T("Open"),                          // lpVerb
                NULL,                                // lpFile
                _T(""),
                _T(""),                              // lpDirectory
                SW_SHOWNORMAL,                       // nShow
                _Module.GetModuleInstance(),         // hInstApp
                (LPVOID)NULL,                        // lpIDList...will set below
                NULL,                                // lpClass
                0,                                   // hkeyClass
                0,                                   // dwHotKey
                NULL                                 // hIcon
            };
            sei.lpIDList = m_ShellMgr.GetFullyQualPidl(lplvid->spParentFolder, lplvid->lpi);
            ::ShellExecuteEx(&sei);
        } else {
            MessageBox(_T("Clicked on folder"));
        }
    }

    return 0L;
}

LRESULT CMainFrame::OnLVDeleteItem(int /*idCtrl*/, LPNMHDR pnmh, BOOL& /*bHandled*/)
{
    LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)pnmh;
    LVITEM lvi;
    lvi.mask = LVIF_PARAM;
    lvi.iItem = pnmlv->iItem;
    lvi.iSubItem = 0;

    if (!m_wndListView.GetItem(&lvi))
        return 0;

    LPLVITEMDATA lplvid = (LPLVITEMDATA)lvi.lParam;
    delete lplvid;
    return 0;
}

LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    PostMessage(WM_CLOSE);
    return 0;
}

LRESULT CMainFrame::OnFileNew(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    // TODO: add code to initialize document
    return 0;
}

LRESULT CMainFrame::OnFileNewWindow(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    ::PostThreadMessage(_Module.m_dwMainThreadID, WM_USER, 0, 0L);
    return 0;
}

LRESULT CMainFrame::OnViewToolBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    static BOOL bNew = TRUE;    // initially visible
    bNew = !bNew;
    ::SendMessage(m_hWndToolBar, RB_SHOWBAND, 1, bNew); // toolbar is band #1
    UISetCheck(ID_VIEW_TOOLBAR, bNew);
    UpdateLayout();
    return 0;
}

LRESULT CMainFrame::OnViewAddressBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    static BOOL bNew = TRUE;    // initially visible
    bNew = !bNew;
    ::SendMessage(m_hWndToolBar, RB_SHOWBAND, 2, bNew); // address bar is band #2
    UISetCheck(ID_VIEW_ADDRESS_BAR, bNew);
    UpdateLayout();
    return 0;
}

LRESULT CMainFrame::OnViewStatusBar(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    BOOL bNew = !::IsWindowVisible(m_hWndStatusBar);
    ::ShowWindow(m_hWndStatusBar, bNew ? SW_SHOWNOACTIVATE : SW_HIDE);
    UISetCheck(ID_VIEW_STATUS_BAR, bNew);
    UpdateLayout();
    return 0;
}

LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
    ::ShellAbout(m_hWnd, _T("WTL Explorer"), _T("This sample program is a part \nof the WTL Library"), AtlLoadIcon(IDR_MAINFRAME));
    return 0;
}

void CMainFrame::InitViews()
{
    // Get Desktop folder
    CShellItemIDList spidl;
    HRESULT hRet = ::SHGetSpecialFolderLocation(m_hWnd, CSIDL_DESKTOP, &spidl);
    hRet;   // avoid level 4 warning
    ATLASSERT(SUCCEEDED(hRet));
    // Get system image lists
    SHFILEINFO sfi = { 0 };
    HIMAGELIST hImageList = (HIMAGELIST)::SHGetFileInfo(spidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_ICON);
    ATLASSERT(hImageList != NULL);
    memset(&sfi, 0, sizeof(SHFILEINFO));
    HIMAGELIST hImageListSmall = (HIMAGELIST)::SHGetFileInfo(spidl, 0, &sfi, sizeof(sfi), SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
    ATLASSERT(hImageListSmall != NULL);
    // Set address bar combo box image list
    m_wndCombo.SetImageList(hImageListSmall);
    // Set tree view image list
    m_wndTreeView.SetImageList(hImageListSmall, 0);
    // Create list view columns
    m_wndListView.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 200, 0);
    m_wndListView.InsertColumn(1, _T("Size"), LVCFMT_RIGHT, 100, 1);
    m_wndListView.InsertColumn(2, _T("Type"), LVCFMT_LEFT, 100, 2);
    m_wndListView.InsertColumn(3, _T("Modified"), LVCFMT_LEFT, 100, 3);
    m_wndListView.InsertColumn(4, _T("Attributes"), LVCFMT_RIGHT, 100, 4);
    // Set list view image lists
    m_wndListView.SetImageList(hImageList, LVSIL_NORMAL);
    m_wndListView.SetImageList(hImageListSmall, LVSIL_SMALL);
}
